const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { HotModuleReplacementPlugin } = require("webpack");
const { VueLoaderPlugin } = require("vue-loader");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// css 压缩
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// css 摇树
const PurgeCSSPlugin = require("purgecss-webpack-plugin");
const glob = require("glob-all");  //匹配路径
// webpack 打包分析
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); // 测量各个插件和 loader 所花费的时间
// dll
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
// happypack
const HappyPack = require("happypack");
const happyThreadPool = HappyPack.ThreadPool({ size: 5 });
// const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) // 根据系统核数分配进程数量

module.exports = {
  entry: "./webpack-optimize-src/index.js",
  output: {
    path: path.resolve(__dirname, "./webpack-optimize-build"),
    filename: "[name]_[chunkhash:6].js",
    // 生产模式才使用
    // publicPath: "https://cdn.company.com/assest/",
  },
  mode: "development",
  devtool: "eval-cheap-module-source-map",
  devServer: {
    port: 9090,
    open: true,
    proxy: {
      "/api": "http://localhost:3000",
    },
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        include: path.resolve(__dirname, "./webpack-optimize-src"),
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        include: path.resolve(__dirname, "./webpack-optimize-src"),
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /\.less$/i,
        include: path.resolve(__dirname, "./webpack-optimize-src"),
        // use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
        // use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader"],
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            // 经过实践：不指定 publicPath 属性，打包出来的css文件也能自动匹配资源路径
            // options: {
            //   publicPath: '../'
            // }
          },
          "css-loader",
          "postcss-loader",
          "less-loader",
        ],
      },
      {
        test: /\.(png|jpe?g|gif|webp)$/,
        type: "asset/resource",
        generator: {
          filename: "assest/[name][hash:6][ext]",
        },
        use: {
          // 图片压缩
          loader: "image-webpack-loader",
        },
      },
      {
        test: /\.(eot|ttf|woff|woff2|svg)$/,
        type: "asset/resource",
        generator: {
          filename: "assest/[name][hash:6][ext]",
        },
      },
      {
        test: /\.m?js$/,
        include: path.resolve(__dirname, "./webpack-optimize-src"),
        use: {
          loader: "babel-loader",
          // 把编译js的任务分配给happypack
          // loader: "HappyPack/loader?id=babel",
        },
      },
      {
        test: /\.vue$/,
        include: path.resolve(__dirname, "./webpack-optimize-src"),
        loader: "vue-loader",
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: "./index.html",
      filename: "index.html",
      //html压缩
      // minify: {
      //   removeComments: true, //移除注释
      //   collapseWhitespace: true, // 移除空格
      //   minifyCSS: true, //压缩内部的css
      // },
    }),
    new HotModuleReplacementPlugin(),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: "css/[name]_[contenthash:6].css",
    }),
    // webpack 4 css 压缩
    // new OptimizeCSSAssetsPlugin({
    //   cssProcessor: require("cssnano"), //引入cssnano配置压缩选项
    //   cssProcessorOptions: {
    //     discardComments: { removeAll: true },
    //   },
    // }),
    // css 摇树
    new PurgeCSSPlugin({
      paths: glob.sync([path.join(__dirname, "./webpack-optimize-src/*.html"), path.resolve(__dirname, "./webpack-optimize-src/*.js")], { nodir: true }),
    }),
    // 分析webpack打包后的模块依赖关系
    // new BundleAnalyzerPlugin()

    // dllplugin 打包第三方类库的动态链接库
    // new AddAssetHtmlWebpackPlugin({
    //   filepath: path.resolve(__dirname, "./dll/react.dll.js"),
    // }),
    // new webpack.DllReferencePlugin({
    //   manifest: path.resolve(__dirname, "./dll/react-manifest.json"),
    // }),
    // new HardSourceWebpackPlugin(),

    // new HappyPack({
    //   // ⽤唯⼀的标识符id，来代表当前的HappyPack是⽤来处理⼀类特定的⽂件
    //   id: "babel",
    //   // 如何处理.js⽂件，⽤法和Loader配置中⼀样
    //   loaders: ["babel-loader"],
    //   threadPool: happyThreadPool,
    // }),
  ],
  // webpack 5 css 压缩
  // optimization: {
  //   minimize: true, // 在开发环境也启用css压缩
  //   minimizer: [new CssMinimizerPlugin()],
  // },

  optimization: {
    // css 压缩
    // minimize: true, // 在开发环境也启用css压缩
    // minimizer: [new CssMinimizerPlugin()],

    // js 摇树
    // usedExports: true,

    // 代码分割
    // splitChunks: {
    //   chunks: "all", //对同步 initial，异步 async，所有的模块有效 all
    //   // minSize: 30000, //最⼩尺⼨，当模块⼤于30kb
    //   // maxSize: 0, //对模块进⾏⼆次分割时使⽤，不推荐使⽤
    //   // minChunks: 1, //打包⽣成的chunk⽂件最少有⼏个chunk引⽤了这个模块
    //   // maxAsyncRequests: 5, //最⼤异步请求数，默认5
    //   // maxInitialRequests: 3, //最⼤初始化请求书，⼊⼝⽂件同步请求，默认3
    //   // automaticNameDelimiter: "-", //打包分割符号
    //   // name: true, //打包后的名称，除了布尔值，还可以接收⼀个函数function
    //   cacheGroups: {
    //     lodash: {
    //       test: /lodash/,
    //       name: "lodash",
    //     },
    //     react: {
    //       test: /react|react-dom/,
    //       name: "react",
    //     },
    //     vue: {
    //       test: /vue/,
    //       name: "vue",
    //     },
    //   },
    // },
    // 作用域提升
    concatenateModules: true,
  },
  resolve: {
    modules: [path.resolve(__dirname, "./node_modules")],
    alias: {
      "@": path.resolve(__dirname, "./webpack-optimize-src"),
      "@assets": path.resolve(__dirname, "./webpack-optimize-src/assets/"),
      "@images": path.resolve(__dirname, "./webpack-optimize-src/images/"),
      "@styles": path.resolve(__dirname, "./webpack-optimize-src/styles/"),
    },
    extensions: [".js", ".json", ".vue"],
  },
};

// 一、优化体验优化
// 1. 省略文件后缀：resolve extensions => 如果配置太多，享受多种类型的文件都省略后缀，会影响构建速度
// 2. 文件夹别名：resolve aliais
// 3. 使用 less 或 sass 作 css 技术栈
// 4. 使用 postcss
// 4.1 autoprefixer: 自动补齐css3样式的浏览器前缀
// 4.2 postcss-plugin0-px2rem：自动 px-to-rem

// 二、优化打包速度
// 最开始：9204 ms
// 1. 缩小 loader 查找文件的范围：loader include => 提升 40%
// 2. 缩小 webpack 查找第三方模块的范围：resolve modules => 小提升
// 3. 分模式打包
// 3.1 webpack-merge：合并 webpack 配置
// 3.2 cross-env：设置环境变量
// 4. DLLPlugin 插件打包第三方类库  3733ms => 3151 ms
// 4.1 优化原理：Dll动态链接库，其实就是做缓存。把一些第三方库（如react）抽离出来，打包到 dll 文件中，在每次打包时，因为需要导入的模块（react）已经存在于某个dll中，所以这个模块就不需要再被打包，而是去dll中获取
// 4.2 webpack4 中实现
// （1）DllPlugin：先打包出一个单独的动态链接库文件
// （2）DllReferencePlugin：引入 DllPlugin 打包好的动态链接库文件
// 4.3 webpack5 中实现：hard-source-webpack-plugin
// 5. 使用 happypack 开启多进程并发执行打包任务
// (1) 适合大项目，小项目开启多进程构建速度可能反而变慢

// 三、优化输出质量
// 1. 静态资源部署
// 1.1 output: publicPath  --> 要部署静态资源的 cdn 地址
// 1.2 MiniCssExtractPlugin 抽离css -> 抽出后可以把 css 文件部署到 cdn 上

// 2. 压缩
// 2.1 html 压缩：html-webpack-plugin 的 minify 配置
// 2.2 css 压缩：
//（1）webpack4 optimize-css-assets-webpack-plugin + cssnano
// (2) webpack5 css-minimize-webpack-plugin
// 2.3 js压缩：mode: 'production'
// 2.4 图片压缩：image-webpack-loader

// 3. 摇树：清除无用的 css 和 js
// 3.1 css 摇树：purify-css purifycss-webpack
// 3.2 js 摇树：使用 webpack 内置 optimization: {useExports: true}
// （1) 只支持 ES Module 引入的方式
// （2）生产模式默认开启
// （3）清除副作用 package.json 设置 sideEffects: ["*.csss"]

// 4. 代码分割
// 4.1 方法一：手动设置多入口（第三方模块单独一个入口）
// 4.2 方法二：使用 webpack 内置 optimization: {splitChunks: {chunks: 'all'}}
// 4.3 方法三：动态导入

// 5. 作用域提升
// optimization.concatenateModules: true：告知 webpack 去寻找模块图形中的片段，哪些是可以安全地被合并到单一模块中；可以让Webpack 打包出来的代码⽂件更⼩
